package edu.amm.api;

/**
 * Функция, заданная на сетке.
 * 
 * @author Иванов Илья
 * @since 2013-05-14
 *
 */
public abstract class MeshFunction {
	
	private Mesh mesh;
	
	public MeshFunction(Mesh mesh) {
		if (mesh == null)
			throw new RuntimeException("Не задана сетка для функции");
		
		this.mesh = mesh;
	}
	
	/**
	 * @return Сетка, на которой задана функция.
	 */
	public Mesh getMesh() {
		return mesh;
	}
	
	/**
	 * @param nodeIndex Индекс узла сетки.
	 * @return Значение функции в узле.
	 */
	public double value(int nodeIndex) {
		if (nodeIndex < 0 || nodeIndex > mesh.getNodesConfig().segmentsCount())
			throw new RuntimeException("Некорректный индекс узла сетки");
		
		return getValue(nodeIndex);
	}
	
	/**
	 * Вычисляет сетчатую функцию в ближайшем к переданному аргументу узле.
	 * 
	 * @param x Аргумент.
	 * @return Значение функции.
	 */
	public double closestValue(double x) {
		double a = mesh.getBounds().getLeftBound();
		double b = mesh.getBounds().getRightBound();
		
		int n = mesh.getNodesConfig().segmentsCount();
		if (x < a)
			return value(0);
		else if (x > b)
			return value(n);
		
		double left;
		double rigth = mesh.node(0);
		for (int i = 1; i <= n; i++) {
			left = rigth;
			rigth = mesh.node(i);
			
			if (x > rigth)
				continue;
			
			return x - left < rigth - x ? value(i - 1) : value(i);
		}
		
		return Double.NaN;
	}
	
	public MeshFunction derivative() {
		throw new UnsupportedOperationException("Производная в явном виде не задана для этой функции"); 
	}
	
	/**
	 * @param nodeIndex Индекс узла сетки.
	 * @return Значение функции в узле.
	 */
	protected abstract double getValue(int nodeIndex);
}